/**************************************************************************************
*
*	Copyright (C) 2009 - 2012 Brent Andrew O. Chua - See LICENSE.txt for more info.
*		
*	Filename	-	ShaderResource.cpp
*
*	Comments	-	See ShaderResource.h
*
**************************************************************************************/
#include "../Include/ShaderResource.h"
#include "../Include/GraphicsDevice.h"
#include "../Include/DepthStencilViewDesc.h"
#include "../Include/ShaderResourceViewDesc.h"
#include "../Include/UnorderedAccessViewDesc.h"

namespace Pulse
{

	ShaderResource::ShaderResource( void )
		: m_shaderResourceType( EShaderResource::UNKNOWN ), m_pSRV( PSX_NULL ), 
		m_pRTV( PSX_NULL ), m_pDSV( PSX_NULL ), m_pUAV( PSX_NULL )
	{

	}

	ShaderResource::~ShaderResource( void )
	{

	}

	void ShaderResource::CreateResourceViews( ID3D11Resource *pResource, UINT bindFlags )
	{
		ID3D11Device *pDevice = (ID3D11Device*)GraphicsDevice::GetInstance()->GetNakedDevice();
		HRESULT hr;

		ReleaseResourceViews();

		if ( bindFlags & D3D11_BIND_SHADER_RESOURCE )
			hr = pDevice->CreateShaderResourceView( pResource, PSX_NULL, &m_pSRV );

		if ( bindFlags & D3D11_BIND_RENDER_TARGET )
			hr = pDevice->CreateRenderTargetView( pResource, PSX_NULL, &m_pRTV );

		if ( bindFlags & D3D11_BIND_DEPTH_STENCIL )
			hr = pDevice->CreateDepthStencilView( pResource, PSX_NULL, &m_pDSV );

		if ( bindFlags & D3D11_BIND_UNORDERED_ACCESS )
			hr = pDevice->CreateUnorderedAccessView( pResource, PSX_NULL, &m_pUAV );
	}

	void ShaderResource::CreateResourceViews( ID3D11Resource *pResource, UINT bindFlags, 
		DepthStencilViewDesc *pDSV, ShaderResourceViewDesc *pSRV, UnorderedAccessViewDesc *pUAV )
	{
		ID3D11Device *pDevice = (ID3D11Device*)GraphicsDevice::GetInstance()->GetNakedDevice();

		ReleaseResourceViews();

		if ( bindFlags & D3D11_BIND_SHADER_RESOURCE )
		{
			if ( pSRV )
			{
				D3D11_SHADER_RESOURCE_VIEW_DESC d3dDesc;

				d3dDesc.Format = DX11Mapping::ToDXBufferFormat( pSRV->m_format );
				d3dDesc.ViewDimension = DX11Mapping::ToDXSRVDimension( pSRV->m_viewDimension );

				switch( pSRV->m_viewDimension )
				{
				case EGraphicsSRVDimension::BUFFER:
					d3dDesc.Buffer.FirstElement = pSRV->m_bufferInfo.firstElement;
					d3dDesc.Buffer.NumElements = pSRV->m_bufferInfo.numElements;
					break;
				case EGraphicsSRVDimension::TEXTURE2D:
					d3dDesc.Texture2D.MostDetailedMip = pSRV->m_tex2DInfo.mostDetailedMip;
					d3dDesc.Texture2D.MipLevels = pSRV->m_tex2DInfo.mipLevels;
					break;
				default:
					PSX_PushError( "View dimension type not yet supported." );
				} 

				pDevice->CreateShaderResourceView( pResource, &d3dDesc, &m_pSRV );
			}
			else
			{
				pDevice->CreateShaderResourceView( pResource, PSX_NULL, &m_pSRV );
			}
		}

		if ( bindFlags & D3D11_BIND_RENDER_TARGET )
		{
			pDevice->CreateRenderTargetView( pResource, PSX_NULL, &m_pRTV );
		}

		if ( bindFlags & D3D11_BIND_DEPTH_STENCIL )
		{
			if ( pDSV )
			{
				D3D11_DEPTH_STENCIL_VIEW_DESC d3dDesc;

				d3dDesc.Format = DX11Mapping::ToDXBufferFormat( pDSV->m_format );
				d3dDesc.ViewDimension = DX11Mapping::ToDXDSVDimension( pDSV->m_dimension );
				d3dDesc.Flags = 0; // TODO: We can optimize by specifying read-only flags. Fix this.

				switch( pDSV->m_dimension )
				{
				case EGraphicsDSVDimension::TEXTURE2D:
					d3dDesc.Texture2D.MipSlice = pDSV->m_tex2D.mipSlice;
					break;
				default:
					PSX_PushError( "Depth stencil view dimension type not yet supported." );
				}

				HRESULT hRet = pDevice->CreateDepthStencilView( pResource, &d3dDesc, &m_pDSV );

				if ( hRet != S_OK )
				{
					PSX_PushError( "Failed to create depth stencil view." );
				}
			}
			else
			{
				pDevice->CreateDepthStencilView( pResource, PSX_NULL, &m_pDSV );
			}
		}

		if ( bindFlags & D3D11_BIND_UNORDERED_ACCESS )
		{
			if ( pUAV )
			{
				D3D11_UNORDERED_ACCESS_VIEW_DESC d3dDesc;

				d3dDesc.Format = DX11Mapping::ToDXBufferFormat( pUAV->m_format );
				d3dDesc.ViewDimension = DX11Mapping::ToDXUAVDimension( pUAV->m_viewDimension );

				switch( pUAV->m_viewDimension )
				{
				case EGraphicsUAVDimension::BUFFER:
					d3dDesc.Buffer.FirstElement = pUAV->m_bufferInfo.firstElement;
					d3dDesc.Buffer.NumElements = pUAV->m_bufferInfo.numElements;
					d3dDesc.Buffer.Flags = DX11Mapping::ToDXEBufferUAVFlags( pUAV->m_bufferInfo.flags );
					break;
				default:
					PSX_PushError( "Depth unordered access view dimension type not yet supported." );
				}

				HRESULT hr = pDevice->CreateUnorderedAccessView( pResource, &d3dDesc, &m_pUAV );

			}
			else
			{
				pDevice->CreateUnorderedAccessView( pResource, PSX_NULL, &m_pUAV );
			}
		}
	}

	void ShaderResource::ReleaseResourceViews( void )
	{
		PSX_SafeRelease( m_pSRV );
		PSX_SafeRelease( m_pRTV );
		PSX_SafeRelease( m_pDSV );
		PSX_SafeRelease( m_pUAV );
	}

}